package isquirrel.recommender;

import isquirrel.UserManager;
import isquirrel.beans.FriendLike;
import isquirrel.beans.Pair;
import isquirrel.beans.Property;
import isquirrel.beans.Url;
import isquirrel.beans.User;
import isquirrel.util.HibernateUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;


public class NetworkRecommender extends Recommender {
	
	public static List<FriendLike> getAllUsersUrls(User u, int howMany) {
		Set<User> allUsers = new HashSet<User>();
		allUsers.addAll(UserManager.getUsers());
		allUsers.remove(u);
		return getFriendsUrls(u, allUsers, howMany);
	}
	
	public static List<FriendLike> getFriendsUrls(User u, int howMany) {
		return getFriendsUrls(u, u.getFriends(), howMany);
	}

	public static List<FriendLike> getFriendsUrls(User u, Set<User> friends, int howMany) {
		List<FriendLike> topUrls; 
		//id for Pair object
		int id = 0;
		//no friends, return null
		if (friends == null || friends.size() == 0 ) {
			return null;
		} else {
			HashMap<Url, Pair> urls = new HashMap<Url, Pair>();
			for (User friend : friends) {
				id++;
				if (friend != null) {
					List<Url> likedUrls = friend.getLikedUrls();
					//this friend hasn't liked any urls yet, proceed to the next friend
					if (likedUrls == null || likedUrls.size() == 0) {
						continue;
					} else {
						double compare = u.compare(friend);
						for (Url url : likedUrls) { 
							if (url != null) {
								//url already exists, increment its "frequency"
								if (urls.containsKey(url))  {
									Pair hashpair = urls.get(url);
									double newCompareValue = hashpair.getValue() + compare;
									urls.put(url, new Pair(newCompareValue, id, hashpair.hits() + 1));
								}
								//url does not exist, just put it in the hashmap
								else {
									urls.put(url, new Pair(compare, id, 1));
								}
							}
						}
					}
				}
			}
			//need to sort hashmap in descending order - not recommended to sort a hashmap :/
			urls = sortHashMap(urls);
			//filter out urls that were already liked by the user
			urls = removeLikedUrls(u,urls);
			//get howMany urls
			topUrls = getTopUrls(urls, howMany);
		}
		return topUrls;
	}
	
	
	/**
	 * Removes all the liked urls (by the user) from the urls hashmap and returns it
	 * @param u
	 * @param urls
	 * @return the filtered hash map
	 */
	private static HashMap<Url, Pair> removeLikedUrls(User u,
			HashMap<Url, Pair> urls) {
		List<Url> likedUrls = HibernateUtil
		.getSession()
		.createQuery(
				"from Url where user_id = ? and liked = ?")
		.setLong(0, u.getUserID())
		.setBoolean(1, true)
		.list();
		
		HashMap<Url, Pair> filteredUrls = new HashMap<Url, Pair>();
		
		for (Url url : urls.keySet()) {
			if (!likedUrls.contains(url)) {
				filteredUrls.put(url, urls.get(url));
			}
		}
		
		return filteredUrls;
	}

	private static List<FriendLike> getTopUrls(HashMap<Url, Pair> mostPopularUrls, int howMany) {
		if (mostPopularUrls == null || mostPopularUrls.size() == 0) { 
			return null;
		}
		else {
			List<FriendLike> topUrls = new ArrayList<FriendLike>();
			Iterator<Url> it = mostPopularUrls.keySet().iterator();
			int count = 0; 
			System.out.println(it.hasNext());
			while (it.hasNext() && count<howMany) {
				System.out.println("Afa");
				Url url = (Url)it.next();
				if (!topUrls.contains(url)) {
					System.out.println(url.getAddress());
					Pair pair = mostPopularUrls.get(url);
					topUrls.add(new FriendLike(url, pair.hits(), pair.getValue()));
					count++;
				}
			}
			return topUrls;
		}
	}

	private static HashMap<Url, Pair> sortHashMap(HashMap<Url, Pair> unsorted){
	    List<Url> mapKeys = new ArrayList<Url>(unsorted.keySet());
	    List<Pair> mapValues = new ArrayList<Pair>(unsorted.values());
	    HashMap<Url, Pair> sortedMap = new LinkedHashMap<Url, Pair>();
	    Collections.sort(mapValues);
	    Object[] sortedArray = mapValues.toArray();
	    int size = sortedArray.length;
	    for (int i=size-1; i>=0; --i){
	        sortedMap.put(mapKeys.get(mapValues.indexOf(sortedArray[i])), 
	                      (Pair)sortedArray[i]);
	    }
	    return sortedMap;
	}
	
}